#pragma once

#include "atom.h"

using namespace System::ComponentModel;
using namespace System::Windows::Forms;
using namespace System::Data;
using namespace System::Drawing;

public ref class Form1 : public System::Windows::Forms::Form
{
	public: Form1(void)
	{
		InitializeComponent();
	}

	~Form1()
	{
		if (components)
		{
			delete components;
		}
	}


	ArrayList^ atoms;
	static array<String^>^ parentTypes = gcnew array<String^>
	{
		"moov",
		"clip",
		"udta",
		"trak",
		"matt",
		"edts",
		"mdia",
		"minf",
		"gmhd",
		"dinf",
		"stbl"
	};

	private: System::Windows::Forms::TreeView^  treeView_atoms;
	private: System::Windows::Forms::TextBox^  textBox_file;
	private: System::Windows::Forms::Button^  button1;
	private: System::ComponentModel::Container ^components;

#pragma region Windows Form Designer generated code
		/// <summary>
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// </summary>
		void InitializeComponent(void)
		{
			this->treeView_atoms = (gcnew System::Windows::Forms::TreeView());
			this->textBox_file = (gcnew System::Windows::Forms::TextBox());
			this->button1 = (gcnew System::Windows::Forms::Button());
			this->SuspendLayout();
			// 
			// treeView_atoms
			// 
			this->treeView_atoms->Anchor = static_cast<System::Windows::Forms::AnchorStyles>((((System::Windows::Forms::AnchorStyles::Top | System::Windows::Forms::AnchorStyles::Bottom) 
				| System::Windows::Forms::AnchorStyles::Left) 
				| System::Windows::Forms::AnchorStyles::Right));
			this->treeView_atoms->Location = System::Drawing::Point(0, 25);
			this->treeView_atoms->Name = L"treeView_atoms";
			this->treeView_atoms->Size = System::Drawing::Size(472, 589);
			this->treeView_atoms->TabIndex = 0;
			// 
			// textBox_file
			// 
			this->textBox_file->Anchor = static_cast<System::Windows::Forms::AnchorStyles>(((System::Windows::Forms::AnchorStyles::Top | System::Windows::Forms::AnchorStyles::Left) 
				| System::Windows::Forms::AnchorStyles::Right));
			this->textBox_file->Enabled = false;
			this->textBox_file->Location = System::Drawing::Point(0, 2);
			this->textBox_file->Name = L"textBox_file";
			this->textBox_file->Size = System::Drawing::Size(399, 20);
			this->textBox_file->TabIndex = 1;
			// 
			// button1
			// 
			this->button1->Anchor = static_cast<System::Windows::Forms::AnchorStyles>((System::Windows::Forms::AnchorStyles::Top | System::Windows::Forms::AnchorStyles::Right));
			this->button1->Location = System::Drawing::Point(405, 2);
			this->button1->Name = L"button1";
			this->button1->Size = System::Drawing::Size(67, 20);
			this->button1->TabIndex = 3;
			this->button1->Text = L"Browse...";
			this->button1->UseVisualStyleBackColor = true;
			this->button1->Click += gcnew System::EventHandler(this, &Form1::click_browse);
			// 
			// Form1
			// 
			this->AutoScaleDimensions = System::Drawing::SizeF(6, 13);
			this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
			this->ClientSize = System::Drawing::Size(472, 613);
			this->Controls->Add(this->button1);
			this->Controls->Add(this->textBox_file);
			this->Controls->Add(this->treeView_atoms);
			this->MaximizeBox = false;
			this->MinimizeBox = false;
			this->MinimumSize = System::Drawing::Size(480, 640);
			this->Name = L"Form1";
			this->ShowIcon = false;
			this->Text = L" QT Atom Explorer";
			this->ResumeLayout(false);
			this->PerformLayout();

		}
#pragma endregion

	private: System::Void click_browse(System::Object^  sender, System::EventArgs^  e)
	{
		OpenFileDialog^ open = gcnew OpenFileDialog();
		open->Filter = "Quick Time Movie (*.mov)|*.mov|Quick Time File (*.qt)|*.qt|All Files (*.*)|*.*";
		if(open->ShowDialog() == Windows::Forms::DialogResult::OK && File::Exists(open->FileName))
		{
			textBox_file->Text = open->FileName;
			load();
		}
	}

	void load()
	{
		atoms = gcnew ArrayList(0);

		FileStream^ fr = File::OpenRead(textBox_file->Text);
		BinaryReader^ br = gcnew BinaryReader(fr, System::Text::Encoding::BigEndianUnicode);

		while(br->BaseStream->Position<br->BaseStream->Length)
		{
			atoms->Add(loadAtom(br));
		}

		// Add nodes recursive
		treeView_atoms->Nodes->Add("ROOT");
		addNodes(atoms, treeView_atoms->Nodes[0]);

		br->Close();
		fr->Close();
	}

	Atom^ loadAtom(BinaryReader^ br)
	{
		Atom^ atom = gcnew Atom();
		array<unsigned char>^ temp = br->ReadBytes(4);
		Array::Reverse(temp);
		atom->Size = BitConverter::ToInt32(temp, 0);
		atom->Type = (System::Text::Encoding::GetEncoding("ASCII"))->GetString(br->ReadBytes(4));
		atom->Children = gcnew ArrayList(0);
		atom->Data = gcnew array<unsigned char>(0);
/*
		if(atom->Size > 1)
		{
			// do nothing
		}
*/
		// We don't need this extended size for our small object movies
		if(atom->Size == 1)
		{
			temp = br->ReadBytes(8);
			Array::Reverse(temp);
			atom->Size = BitConverter::ToInt64(temp, 0)-4;
		}
		if(atom->Size == 0)
		{
			atom->Size = br->BaseStream->Length - br->BaseStream->Position + 8;
		}

		// load childs until parent size is compeletely filled (including the header 8 byte)
		__int64 SizeLeft = atom->Size;
		while(SizeLeft>8)
		{
			SizeLeft = SizeLeft - loadAtomChildren(br, atom);
			// atom type udta (userdata) sometimes contains a tailing 0 (int32) value
			if(atom->Type == "udta" && SizeLeft == 12)
			{
				SizeLeft = 0;
				br->ReadBytes(4);
			}
		}

		return atom;
	}

	__int64 loadAtomChildren(BinaryReader^ br, Atom^ Parent)
	{
		for(int i=0; i<parentTypes->Length; i++)
		{
			if(parentTypes[i] == Parent->Type)
			{
				Atom^ atom = loadAtom(br);
				Parent->Children->Add(atom);
				return atom->Size;
			}
		}
		Parent->Data = br->ReadBytes((int)(Parent->Size-8));
		return Parent->Size;
	}

	void addNodes(ArrayList^ atoms, TreeNode^ node)
	{
		for(int i=0; i<atoms->Count; i++)
		{
			node->Nodes->Add(((Atom^)atoms[i])->Type + " [" + ((Atom^)atoms[i])->Size.ToString() + "]");
			if(((Atom^)atoms[i])->Children->Count>0)
			{
				addNodes(((Atom^)atoms[i])->Children, node->Nodes[i]);
			}
		}
	}
};